use cfw_kit::uart::{Error, Uart, UartContext};
use periph_utils::{stm32f1, DmaConfig, GpioConfig, UartConfig, UartInit};
use periph_utils::stm32f1::stm32f103::{NVIC, Interrupt, interrupt };
use proto_bytes::message::MessageRef;
use proto_bytes::proto2::ToProtoBytes2;

#[derive(GpioConfig, UartConfig)]
#[gpio(
    init = "gpio_init",
    io(name = "tx", io = "PA9", mode = "AF_PP"),
    io(name = "rx", io = "PA10", mode = "IN_FLOAT")
)]
#[uart(init = "Config", uart = 1, baud_rate = 115200)]
struct Init;

const WRITE_BUF_SIZE: usize = 255;
const RECV_BUF_SIZE: usize = 255;

impl Uart for Init {
    fn recv_with_dma(recv_buf: &[u8]) {
        Config::usart1_rx_dma1_ch5_read(
            recv_buf.as_ptr() as _,
            USART1.dr.as_ptr() as _, 
            recv_buf.len() as _,
        );
    }

    fn rx_index() -> usize {
        RECV_BUF_SIZE - (Config::usart1_rx_dma1_ch5_ndtr() as usize)
    }

    fn write_with_dma(write_buf: &[u8]) {
        Config::usart1_tx_dma1_ch4_write(
            write_buf.as_ptr() as _,
            USART1.dr.as_ptr() as _,
            write_buf.len() as _,
        );
    }
}

static CTX: UartContext<WRITE_BUF_SIZE, RECV_BUF_SIZE, Init> = UartContext::uninit();

pub unsafe fn init() {
    Init::gpio_init();
    Config::dma_init();
    Config::usart1_init();

    CTX.init_once("cmd_uart");
    NVIC::unmask(Interrupt::USART1);
    NVIC::unmask(Interrupt::DMA1_CHANNEL4);
}

#[inline(always)]
pub fn write<T: ToProtoBytes2>(x: T) {
    unsafe { CTX.write(x); }
}

#[inline(always)]
pub fn read_message<'a>(buf: &'a mut [u8]) -> Result<MessageRef<'a>, Error> {
    CTX.read_message(buf)
}


#[interrupt]
unsafe fn USART1() {
    if Config::usart1_is_idle() {
        Config::usart1_clear_idle();
        CTX.on_idle();
    }
}

#[interrupt]
unsafe fn DMA1_CHANNEL4() {
    if Config::usart1_tx_dma1_ch4_is_tc() {
        Config::usart1_tx_dma1_ch4_clear_flags();
        CTX.on_tc();
    }
}




